#include "../process.h"
#include "../util.h"
#include "../config.h"

#define BATCH 20000

inline bool is_run(int year)
{
	return (!year%100)?(!year%400):(!year%4);
}

inline bool year2sec(int year)
{
	int res = 0;
	for(int i = 0; i < year; ++ i)
	{
		if(is_run)
			res += 31622400;
		else
			res += 31536000;
	}
	return res;
}

inline int month2sec(int month, int is_run)
{
	int res = 0;
	for(int i = 1; i < month; ++ i)
	{
		if(month == 1 || month == 3|| month == 5|| month == 7|| month == 8|| month == 10|| month == 12)
			res += 2678400;
		else if(month == 4|| month == 6|| month == 9|| month == 11)
			res += 2592000;
		else if(month == 2)
			res += is_run?2505600:2822400;
	}
	return res;
}

inline int day2sec(int date)
{
	return (date - 1)*216000;
}

inline int hour2sec(int hour)
{
	return 3600*hour;
}

inline int min2sec(int min)
{
	return 60*min;
}

inline int parse_time(string time_s)
{
	vector<string> time_v = util::tokenize(time_s, ": -");
	int year = atoi(time_v[0].c_str());
	int month = atoi(time_v[1].c_str());
	int day = atoi(time_v[2].c_str());
	int hour = atoi(time_v[3].c_str());
	int min = atoi(time_v[4].c_str());
	int sec = atoi(time_v[5].c_str());
	return year2sec(year) + month2sec(month, is_run(year)) + day2sec(day) + hour2sec(hour) + min2sec(min) + sec;
}

inline void display(iostream *out, map<string, set<string>> &graph)
{
	map<string, set<string>>::iterator itr_q;
	for(itr_q = graph.begin(); itr_q != graph.end(); ++ itr_q)
	{
		*out << (*itr_q).first << ":";
		set<string> &e_s = (*itr_q).second;
		set<string>::iterator itr_e;
		for(itr_e = e_s.begin(); itr_e != e_s.end(); ++ itr_e)
			*out << *itr_e << " | ";
		*out << endl;
	}
}

int main(int argc, char *argv[])
{
	fstream input(argv[1], fstream::in);
	fstream output(argv[2], fstream::out);
	fstream config(argv[3], fstream::in);

	Config::load(&config);

	string prev_user_id, cur_user_id;
	int count = 0;
	bool first_line = true;
	double session_count = 0;
	vector<pair<string, int>> collection;
	vector<string> record_line;
	vector<vector<int>> sessions;
	vector<double> sessions_time;
	vector<double> sessions_queries;
	int rare_session = 0, dense_session = 0;
	while(!input.eof())
	{
		string line;
		getline(input, line);
		if(line.length() == 0) continue;
		if(count++%BATCH == 0)cout << ".";
		vector<string> v = util::tokenize(line, "\t");
		cur_user_id = v[0];
		if(first_line)
		{
			prev_user_id = cur_user_id;
			first_line = false;
		}
		if(cur_user_id != prev_user_id)
		{
			for(int i = 0; i < collection.size(); ++ i)
			{
				sessions.push_back(vector<int>());
				sessions.back().push_back(i);
				sessions_time.push_back(0);
				sessions_queries.push_back(1);
				int t_i = collection[i].second;
				for(int j = i + 1; j < collection.size(); ++ j)
				{
					int t_j = collection[j].second;
					if(t_j - t_i < atoi(Config::get("Session_Length").c_str()) && t_j - t_i >= 0){
						sessions.back().push_back(j);
						sessions_time.back() += t_j - t_i;
						sessions_queries.back() ++;
					}
					else
					{
						i = j;
						break;
					}
				}
			}
			for(int i = 0; i < sessions.size(); ++ i)
			{
				if(sessions[i].size() > atoi(Config::get("Session_Size").c_str()))
				{
					dense_session ++;				
					for(int j = 0; j < sessions[i].size(); ++ j)
						output << record_line[sessions[i][j]] << endl;
				}
				else
					rare_session ++;
			}
			sessions.clear();
			collection.clear();
			record_line.clear();
			prev_user_id = cur_user_id;
		}
		string query = v[1];
		int q_time = parse_time(v[2]);
		collection.push_back(pair<string, int>(query, q_time));
		record_line.push_back(line);
	}
	for(int i = 0; i < collection.size(); ++ i)
	{
		sessions.push_back(vector<int>());
		sessions.back().push_back(i);
		sessions_time.push_back(0);
		sessions_queries.push_back(1);
		int t_i = collection[i].second;
		for(int j = i + 1; j < collection.size(); ++ j)
		{
			int t_j = collection[j].second;
			if(t_j - t_i < atoi(Config::get("Session_Length").c_str()) && t_j - t_i >= 0)
			{
				sessions.back().push_back(j);
				sessions_time.back() += t_j - t_i;
				sessions_queries.back() ++;
			}
			else
			{
				i = j;
				break;
			}
		}
	}
	for(int i = 0; i < sessions.size(); ++ i)
	{
		if(sessions[i].size() > atoi(Config::get("Session_Size").c_str()))
		{
			dense_session ++;
			for(int j = 0; j < sessions[i].size(); ++ j)
				output << record_line[sessions[i][j]] << endl;
		}
		else
			rare_session ++;
	}
	cout << "Total Sessions\t" << dense_session + rare_session << endl;
	cout << "Dense Sessions\t" << dense_session << endl;
	cout << "Rare Sessions\t" << rare_session << endl;
	double avg_time = 0.0, avg_query = 0.0, max_time = 0.0, max_query = 0.0;
	for(int i = 0; i < sessions_time.size(); ++ i)
		avg_time += sessions_time[i];
	avg_time/=sessions_time.size();
	for(int i = 0; i < sessions_queries.size(); ++ i)
		avg_query += sessions_queries[i];
	avg_query/=sessions_time.size();
	cout << "Average time\t" << avg_time << endl;
	cout << "Average query\t" << avg_query << endl;
	sessions.clear();
	collection.clear();
	record_line.clear();
	input.close();
	output.close();
	return 0;
}